package com.computer.spring.framework.webmvc.servlet;

import com.computer.spring.framework.annotation.BYController;
import com.computer.spring.framework.annotation.BYRequestMapping;
import com.computer.spring.framework.context.BYApplicationContext;
import com.computer.spring.framework.webmvc.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description
 * @Author MagicIce
 * @Date 2020/11/22 9:36
 */
public class BYDispatcherServlet extends HttpServlet {

    private final String LOCATION = "contextConfigLocation";

    private List<BYHandlerMapping> handlerMappings = new ArrayList<>();

    private Map<BYHandlerMapping, BYHandlerAdapter> handlerAdapters = new HashMap<>();

    private List<BYViewResolver> viewResolvers = new ArrayList<>();

    private BYApplicationContext context;

    @Override
    public void init(ServletConfig config) throws ServletException {
        context = new BYApplicationContext(config.getInitParameter(LOCATION));
        initStrategies(context);
    }

    protected void initStrategies(BYApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        /** HandlerMappings 用来保存Controller 中配置的 RequestMapping 和 Method 的对应关系；通过 HandlerMapping 将请求映射到处理器 */
        initHandlerMappings(context);
        /** HandlerAdapters 用来动态匹配 Method 参数，包括类转换、动态赋值；通过 HandlerAdapter 进行多类型的参数动态匹配 */
        initHandlerAdapters(context);
        /** 如果执行官过程中遇到异常，交给 HandlerExceptionResolver 来解析 */
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

    private void initFlashMapManager(BYApplicationContext context) {
    }

    private void initViewResolvers(BYApplicationContext context) {
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        File templateRootDir = new File(templateRootPath);

        for (File template : templateRootDir.listFiles()){
            this.viewResolvers.add(new BYViewResolver(templateRoot));
        }
    }

    private void initRequestToViewNameTranslator(BYApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(BYApplicationContext context) {
    }

    private void initHandlerAdapters(BYApplicationContext context) {
        /**
         * 在初始化阶段，将这些参数的名字或者类型按一定的顺序保存下来，因为后面用反射调用的时候，传的形参是一个数组
         * 可以通过记录这些参数的位置 index，逐个从数组中取值，这样就和参数的顺序无关了
         */
        for (BYHandlerMapping handlerMapping : this.handlerMappings){
            /** 每个方法有一个参数列表，这里保存的是形参列表 */
            this.handlerAdapters.put(handlerMapping, new BYHandlerAdapter());
        }
    }

    /** 将 Controller 中配置的 RequestMapping 和 Method 进行一一对应 */
    private void initHandlerMappings(BYApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            for (String beanName : beanNames){
                Object controller = context.getBean(beanName);
                Class<?> clazz = controller.getClass();
                if (!clazz.isAnnotationPresent(BYController.class)){
                    continue;
                }
                String baseUrl = "";
                if (clazz.isAnnotationPresent(BYRequestMapping.class)){
                    BYRequestMapping requestMapping = clazz.getAnnotation(BYRequestMapping.class);
                    baseUrl = requestMapping.value();
                }

                /** 扫描所有的 public 类型的方法 */
                Method[] methods = clazz.getMethods();
                for (Method method : methods){
                    if (!method.isAnnotationPresent(BYRequestMapping.class)){
                        continue;
                    }

                    BYRequestMapping requestMapping = method.getAnnotation(BYRequestMapping.class);
                    String regex = ("/" + baseUrl + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);
                    this.handlerMappings.add(new BYHandlerMapping(controller, method, pattern));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initThemeResolver(BYApplicationContext context) {
    }

    private void initLocaleResolver(BYApplicationContext context) {
    }

    private void initMultipartResolver(BYApplicationContext context) {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req, resp);
        } catch (Exception e) {
            resp.getWriter().write("<font size='25' color='blue'>500 Exception</font><br/>Detail:<br/>" +
                    Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "")
                    .replaceAll("\\s", "\r\n") + "<font color='green'><i>Copyright</i></cont>"
            );
        }
    }

    private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{
        BYHandlerMapping handlerMapping = getHandler(request);
        if (null == handlerMapping){
            processDispatchResult(request, response, new BYModelAndView("404"));
            return;
        }

        BYHandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);

        BYModelAndView mv = handlerAdapter.handle(request, response, handlerMapping);

        processDispatchResult(request, response, mv);
    }

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, BYModelAndView mv) throws Exception{
        /** 调用 viewResolver 的 resolveViewName() 方法 */
        if (null == mv){
            return;
        }

        if (this.viewResolvers.isEmpty()){
            return;
        }

        if (this.viewResolvers != null){
            for (BYViewResolver viewResolver : this.viewResolvers){
                BYView view = viewResolver.resolveViewName(mv.getViewName(), null);
                if (null != view){
                    view.render(mv.getModel(), request, response);
                    return;
                }
            }
        }
    }

    private BYHandlerAdapter getHandlerAdapter(BYHandlerMapping handlerMapping){
        if (this.handlerAdapters.isEmpty()){
            return null;
        }
        BYHandlerAdapter handlerAdapter = this.handlerAdapters.get(handlerMapping);
        if (handlerAdapter.supports(handlerMapping)){
            return handlerAdapter;
        }
        return null;
    }

    private BYHandlerMapping getHandler(HttpServletRequest request){
        if (this.handlerMappings.isEmpty()){
            return null;
        }

        String url = request.getRequestURI();
        String contextPath = request.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for (BYHandlerMapping handlerMapping : this.handlerMappings){
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if (!matcher.matches()){
                continue;
            }
            return handlerMapping;
        }
        return null;
    }
}
